﻿package com.reyco1.audio
{
	/*
	imports
	*/
	import com.reyco1.extension.DispatchExtension;
	import flash.media.SoundChannel;
	import com.reyco1.util.IntervalUtil;
	import flash.media.Sound;
	import flash.utils.Dictionary;
	import com.reyco1.events.AudioCuePointEvent;
	import flash.events.Event;
	
	/*
	class
	*/
	public class AudioCuePoint extends DispatchExtension
	{
		/*
		variables
		*/
		private var sndObject:Sound;
		private var sndChannel:SoundChannel;
		private var positionTimer:IntervalUtil;
		private var sndDuration:Number;
		private var sndPosition:Number;
		private var arrCuePoints:Array;
		private var currentCuePointIndex:Number;
		
		/*
		constructor
		*/
		public function AudioCuePoint():void
		{
			this.currentCuePointIndex = 0;
			this.arrCuePoints = new Array();
		}
		
		/**
		 * Adds a cue point.
		 * @param $cuePntObj : an object with two mandatory values:
		 * 
		 * <ul>
		 * <li><b>time</b> : Number - time in seconds of where to place the cure point</li>
		 * <li><b>name</b> : String - a unique name for the cue point being added</li>
		 * </ul>
		 * 
		 */		
		public function addCuePoint($cuePntObj:Object, $fireEvent:Boolean = true):void
		{
			if($cuePntObj.name == undefined || $cuePntObj.time == undefined){
				throw new Error("The AudioCuePoint constructor expects an object with two values: name and time.");
			}
			this.arrCuePoints.push($cuePntObj);
			if($fireEvent){
				this.dispatchEvent(new AudioCuePointEvent(AudioCuePointEvent.CUE_POINT_ADDED, {name:$cuePntObj.name}));
			}
		}
		
		/**
		 * Removes a cue point added using the addCuePoint method.
		 * @param $cuePointName : String - naem of the cue point to be removed
		 * 
		 */		
		public function removeCuePoint($cuePointName:String, $fireEvent:Boolean = true):void
		{
			var arrTemp:Array = new Array();
			for(var a:uint = 0; a<this.arrCuePoints.length; a++){
				if(this.arrCuePoints[a] != $cuePointName){
					arrTemp.push(arrCuePoints)
				}
			}
			this.arrCuePoints = arrTemp;
			if($fireEvent){
				this.dispatchEvent(new AudioCuePointEvent(AudioCuePointEvent.CUE_POINT_REMOVED, {name:$cuePointName}));
			}
		}
		
		/**
		 * Removes all cue points set with the addCuePoint method
		 * 
		 */		
		public function removeAllCuePoints():void
		{
			for(var a:uint = 0; a<this.arrCuePoints.length; a++){
				this.removeCuePoint(this.arrCuePoints[a].name, false);
			}
			this.arrCuePoints = new Array();
			this.dispatchEvent(new AudioCuePointEvent(AudioCuePointEvent.ALL_CUE_POINTS_REMOVED, {}));
		}
		
		/**
		 * Moves a cue point already added with the addCuePoint method to a new time.
		 * @param $cuePointName : String - name of the cue point to move
		 * @param $newTime : Number - new time to move the cue point to
		 * 
		 */		
		public function moveCuePoint($cuePointName:String, $newTime:Number):void
		{
			this.removeCuePoint($cuePointName, false);
			this.addCuePoint({name:$cuePointName, time:$newTime}, false);
			this.dispatchEvent(new AudioCuePointEvent(AudioCuePointEvent.CUE_POINT_MOVED, {name:$cuePointName}));
		}
		
		/**
		 * Allows the AudioCuePoint instance to listen to the sound as it plays in order to get the 
		 * position and fire the Auidio.CUE_POINT event once it reaches one of the registered cue points.
		 * 
		 */		
		public function listen($soundChannel:SoundChannel):void
		{
			this.positionCheck($soundChannel);
		}	
		
		/**
		 * Resets the AudioCuePoint isntance to start listening to the cuepoints from the begining.
		 * 
		 */		
		public function reset():void
		{
			this.arrCuePoints.sortOn("time", Array.DESCENDING);
			this.currentCuePointIndex = 0;
		}
		
		/**
		 * Boolean which states if the the song has cuepoints
		 * @return : Boolean
		 * 
		 */				
		public function get hasCuePoints():Boolean
		{
			return (this.arrCuePoints.length > 0) ? true : false;
		}
		
		private function positionCheck($soundChannel:SoundChannel):void
		{
			this.sndPosition = Math.round($soundChannel.position * .001);			
			if(this.arrCuePoints[this.currentCuePointIndex] != undefined){
				if(this.sndPosition == this.arrCuePoints[this.currentCuePointIndex].time){
					var objParams:Object = new Object();
					objParams.name = this.arrCuePoints[this.currentCuePointIndex].name;
					objParams.owner = this.arrCuePoints[this.currentCuePointIndex].owner;
					objParams.time = this.sndPosition;
					this.currentCuePointIndex++;
					this.dispatchEvent(new AudioCuePointEvent(AudioCuePointEvent.CUE_POINT, objParams));
				}
			}
		}
	}
}